package com.erdos.upc.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpSession;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.erdos.common.util.MenuConfig;
import com.erdos.common.web.LoginUtil;
import com.erdos.core.entity.Facility;
import com.erdos.core.service.FacilityService;
import com.erdos.upc.core.UpcConst;
import com.erdos.upc.core.UpcUser;
import com.erdos.upc.dao.UserDao;
import com.erdos.upc.encode.PasswordEncode;
import com.erdos.upc.entity.MenuVO;
import com.erdos.upc.entity.Privilege;
import com.erdos.upc.entity.User;
import com.erdos.upc.service.AuthService;
import com.erdos.upc.service.BusinessPrivilegeService;
import com.erdos.upc.service.PrivilegeService;

public class AuthServiceImpl implements AuthService {


	private static final Log logger = LogFactory.getLog(AuthServiceImpl.class);

	private final static String MENU = "Menu";
	private final static String MENU_GROUP = "MenuGroup";
	private final static String MENU_ITEM = "MenuItem";
	private final static String PRIVILEGE_CODE = "privilegeCodes";
	private final static String REMOVE_FLAG = "removed";
	private final static String ATTRIBUTE_NAME = "name";
	private boolean dev = false;
	
	public void setDev(boolean dev) {
		this.dev = dev;
	}
	private PrivilegeService privilegeService;

	private UserDao userDao;

	private PasswordEncode passwordEncode;
	
	private BusinessPrivilegeService businessPrivilegeService;
	
	private FacilityService facilityService;

	public UserDao getUserDao() {
		return userDao;
	}

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	public PasswordEncode getPasswordEncode() {
		return passwordEncode;
	}

	public void setPasswordEncode(PasswordEncode passwordEncode) {
		this.passwordEncode = passwordEncode;
	}

	public void setPrivilegeService(PrivilegeService privilegeService) {
		this.privilegeService = privilegeService;
	}

	/* (non-Javadoc)
	 * @see com.hp.upc.service.AuthService#authUpcUser(Map, com.hp.upc.entity.User)
	 */
	public UpcUser authUpcUser(Map<String, Object> params, User user) {
		UpcUser upcUser = new UpcUser();
		Set<Privilege> totalPrivileges = new HashSet<Privilege>();
		for (Privilege privilege : privilegeService
				.findAllPrivilegeByUserSid(user.getSid())) {
			totalPrivileges.add(privilege);
		}
		upcUser.setPrivileges(totalPrivileges);
		if (logger.isDebugEnabled()) {
			logger.debug("list granted privilege...");
			for (Privilege privilege : upcUser.getPrivileges()) {
				logger.debug(privilege.getCode()+":"+ privilege.getName() + ":" + privilege.getUrl());
			}
			logger.debug("list granted privilege[done]");
		}
		String authLoginId = user.getPin();
		upcUser.setLoginId(authLoginId);
		upcUser.setLoginTime(new Date());
		upcUser.setLoginIp((String) params.get("loginIp"));
		upcUser.setLoginGroup(user.getLoginGroup());
		upcUser.setFacilityNo(user.getFacilityNo());
		
		return upcUser;
	}

	public void init() {
		System.out.print("sss");

	}

	/**
	 * 获得菜单对象列表
	 */
	public List<MenuVO> authUserMenu(UpcUser upcUser) {
		List<MenuVO> menus = new ArrayList<MenuVO>();
		Set<String> priFuncIds = new HashSet<String>();
		if (upcUser != null) {
			for (Privilege p : upcUser.getPrivileges()) {
				priFuncIds.add(p.getCode());
			}
		}
		if (dev) {
			Document xmlDocument = getDocumentFromXml();
			menus = getMenuFromXml(xmlDocument).getChildren();
		} else {
			if (priFuncIds != null && !priFuncIds.isEmpty()) {
				Document xmlDocument = getDocumentFromXml();
				if (xmlDocument != null) {
					// check every menuItem, mark its name attribute as
					// "removed"
					// if it is not in the set of priFuncIds
					filterUnadmittedFunction(xmlDocument, priFuncIds);

					// check every menuGroup, mark its name attribute as
					// "removed"
					// if it is a empty group(which has no childNode)
					filterEmptyGroups(xmlDocument);
				}
				menus = getMenuFromXml(xmlDocument).getChildren();
			}
		}
		return menus;

	}

	/**
	 * 获取document
	 * @return
	 */
	private Document getDocumentFromXml() {
		Document xmlDocument = null;
		try {
			// parse source menu xml file
			xmlDocument = MenuConfig.getXml();
		} catch (ParserConfigurationException e) {
			logger.error(e);
			return null;
		} catch (SAXException e) {
			logger.error(e);
			return null;
		} catch (IOException e) {
			logger.error(e);
			return null;
		}

		return xmlDocument;
	}

	/**
	 * 根据用户权限过滤xml菜单
	 * @param xmlDocument
	 * @param priFuncIds
	 */
	private void filterUnadmittedFunction(Document xmlDocument,
			Set<String> priFuncIds) {
		logger.debug("filterUnadmittedFunction run!");
		NodeList menugroups = xmlDocument.getDocumentElement().getChildNodes();

		if (menugroups != null && menugroups.getLength() > 0) {
			for (int i = 0; i < menugroups.getLength(); i++) {
				Node node = menugroups.item(i);
				filterNode(node, priFuncIds);
			}
		}

	}

	/**
	 * 根据权限列表过滤节点
	 * @param node
	 * @param priFuncIds
	 */
	private void filterNode(Node node, Set<String> priFuncIds) {
		// logger.debug("filterNode run!");

		if (null != node && node.getNodeType() == Node.ELEMENT_NODE) {
			if (node.getNodeName().equals(MENU_ITEM)) {
				NamedNodeMap attributes = node.getAttributes();
				/*logger.debug("node type: " + node.getNodeName() 
						+ "name: "+attributes.getNamedItem(ATTRIBUTE_NAME).getNodeValue());*/
				if (attributes != null
						&& attributes.getNamedItem(PRIVILEGE_CODE) != null) {
					String functionId = attributes.getNamedItem(PRIVILEGE_CODE)
							.getNodeValue();
					if (!priFuncIds.contains(functionId)) {
						// mark name attribute as removed
						attributes.getNamedItem(ATTRIBUTE_NAME).setNodeValue(
								REMOVE_FLAG);
					}
				}
			} else if (node.getNodeName().equals(MENU_GROUP)) {
				NodeList childNodes = node.getChildNodes();
				/*logger.debug("node type: "+ node.getNodeName() + "child notes: " 
						+ childNodes.getLength());*/
				if (childNodes != null && childNodes.getLength() > 0) {
					for (int i = 0; i < childNodes.getLength(); i++) {
						Node childNode = childNodes.item(i);
						filterNode(childNode, priFuncIds);
					}
				}
			} else {
				logger.error("Name of node: " + node.getNodeName()
						+ " is wrong !");
			}
		}
	}

	/**
	 * 过滤空的group
	 * @param xmlDocument
	 */
	private void filterEmptyGroups(Document xmlDocument) {
		Node topItem = xmlDocument.getDocumentElement();
		Set<Node> lowEndGroups = new HashSet<Node>();

		// find low-end groups which doesn't contain a sub group
		findLowEndGroups(topItem, lowEndGroups);

		// 1) mark low end groups whoes child nodes are all "removed" as
		// "removed".
		// 2) add the parent nodes of these low end groups to parentNodes set.
		// 3) if parentNodes set contains only top item "Menu", function is
		// finished,
		// otherwise do this function again.
		markEmptyGroups(lowEndGroups);
	}

	/**
	 *查找node下所有group
	 * @param node
	 * @param result
	 */
	private void findLowEndGroups(Node node, Set<Node> result) {
		logger.debug("findLowEndGroups run!");
		if (!node.getNodeName().equals(MENU_ITEM)) {
			NodeList menugroups = node.getChildNodes();
			if (menugroups != null && menugroups.getLength() > 0) {
				// if this node is a empty group
				boolean isLowEndGroupFlag = true;
				for (int i = 0; i < menugroups.getLength(); i++) {
					Node childNode = menugroups.item(i);
					if (childNode.getNodeName().equals(MENU_GROUP)) {
						isLowEndGroupFlag = false;
						findLowEndGroups(childNode, result);
					}
				}
				if (isLowEndGroupFlag) {
					result.add(node);
				}
			} else {
				// if this is a group has no child
				result.add(node);
			}
		}
	}

	/**
	 * 标记空组
	 * @param groupNodes
	 */
	private void markEmptyGroups(Set<Node> groupNodes) {
		logger.debug("markEmptyGroups run!");
		Set<Node> parentNodes = new HashSet<Node>();

		Iterator<Node> it = groupNodes.iterator();
		while (it.hasNext()) {
			Node group = (Node) it.next();
			// if group is not the top item
			if (!group.getNodeName().equals(MENU)) {
				// if group is a empty group, mark its name with removed flag
				if (isEmptyGroup(group)) {
					NamedNodeMap attributes = group.getAttributes();
					if (attributes != null) {
						attributes.getNamedItem(ATTRIBUTE_NAME).setNodeValue(
								REMOVE_FLAG);
					}
				}
				parentNodes.add(group.getParentNode());
			}
		}

		if (parentNodes.isEmpty() || isTopItem(parentNodes)) {
			return;
		} else {
			markEmptyGroups(parentNodes);
		}
	}

	/**
	 * 是否是空组
	 * @param groupNode
	 * @return
	 */
	private boolean isEmptyGroup(Node groupNode) {
		NodeList childNodes = groupNode.getChildNodes();
		if (childNodes != null && childNodes.getLength() > 0) {
			for (int i = 0; i < childNodes.getLength(); i++) {
				Node childNode = childNodes.item(i);
				NamedNodeMap attributes = childNode.getAttributes();

				if (attributes != null) {
					if (!REMOVE_FLAG.equals(attributes.getNamedItem(
							ATTRIBUTE_NAME).getNodeValue())) {
						return false;
					}
				}
			}
		}
		return true;
	}

	/**
	 * 是否是最高的item
	 * @param nodes
	 * @return
	 */
	private boolean isTopItem(Set<Node> nodes) {
		if (nodes.size() == 1) {
			Node node = (Node) nodes.toArray()[0];
			if (node.getNodeName().equals(MENU)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 从document 获得MenuVO对象
	 * @param xml
	 * @return
	 */
	private MenuVO getMenuFromXml(Document xml) {
		MenuVO menu0 = new MenuVO();
		NodeList menuGroup = xml.getDocumentElement().getChildNodes();
		if (menuGroup != null && menuGroup.getLength() > 0) {
			for (int i = 0; i < menuGroup.getLength(); i++) {
				node2Menu(menuGroup.item(i), menu0);
			}
		}
		return menu0;
	}

	/**
	 * 从node转换MenuVO对象
	 * @param node
	 * @param parent
	 */
	private void node2Menu(Node node, MenuVO parent) {
		NamedNodeMap attribute = null;
		if (null != node && node.getNodeType() == Node.ELEMENT_NODE) {

			if (node.getNodeName().equals(MENU_ITEM)) {
				attribute = node.getAttributes();
				// Start element MenuItem.
				if (attribute != null
						&& !attribute.getNamedItem(ATTRIBUTE_NAME)
								.getNodeValue().equals(REMOVE_FLAG)) {
					MenuVO menu01 = new MenuVO();
					menu01.setName(attribute.getNamedItem("name")
							.getNodeValue());
					menu01.setCode(attribute.getNamedItem("code")
							.getNodeValue());
					menu01.setHref(attribute.getNamedItem("destURL")
							.getNodeValue());
					menu01.setParent(parent);
					parent.getChildren().add(menu01);
				}
			}

			if (node.getNodeName().equals(MENU_GROUP)) {
				attribute = node.getAttributes();
				if (attribute != null
						&& !attribute.getNamedItem(ATTRIBUTE_NAME)
								.getNodeValue().equals(REMOVE_FLAG)) {
					// Start element MenuGroup.
					MenuVO menu02 = new MenuVO();
					menu02.setName(attribute.getNamedItem("name")
							.getNodeValue());
					menu02.setCode(attribute.getNamedItem("code")
							.getNodeValue());
					menu02.setParent(parent);
					parent.getChildren().add(menu02);
					NodeList children = node.getChildNodes();
					if (children != null && children.getLength() > 0) {
						for (int j = 0; children != null
								&& j < children.getLength(); j++) {
							node2Menu(children.item(j), menu02);
						}
					}
				}
			}
		}
	}

	public void changePasswd(String pin, String password) {
		String encodPassword = null;
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("pin", pin);
		User user = this.userDao.findUniqueByParams(params);
		if (StringUtils.isNotEmpty(password)) {
			encodPassword = passwordEncode.encode(password);
			user.setPassword(encodPassword);
		}
		this.userDao.update(user);
	}

	public boolean checkPasswdExist(String passwd, HttpSession session) {
		logger.debug("passwd:" + passwd);
		String pin = LoginUtil.getUpcUserPin();
		logger.debug("user pin:" + LoginUtil.getUpcUserPin());
		if (StringUtils.isNotEmpty(passwd) && StringUtils.isNotEmpty(pin)) {
			Map<String, Object> params = new HashMap<String, Object>();
			params.put("pin", pin);
			User user = userDao.findUniqueByParams(params);
			logger.debug("user passwd:" + user.getPassword());
			if (passwordEncode.encode(passwd).equals(user.getPassword())) {
				return true;
			} else {
				return false;
			}
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.hp.upc.service.AuthService#getFacilityIds(java.lang.String)
	 */
	private String findFacilityIds(Long userSid) {
		List<String> sidLs = this.businessPrivilegeService.findIDByUserAndCategory(userSid, null, UpcConst.DATA_PRIVILDGE_FACILITY_ID);
		StringBuilder facilitySids = new StringBuilder("");
		StringBuilder facilityIds = new StringBuilder("");
		for(String facilitySid:sidLs){
			facilitySids.append("").append(facilitySid).append(",");
		}
		if(facilitySids.length()>0){
			facilitySids.deleteCharAt(facilitySids.length()-1);
			Map<String,Object> params = new HashMap<String,Object>();
			params.put("sids", facilitySids.toString());
			List<Facility> facilities = facilityService.findByParams(params);
			for(Facility facility:facilities){
				facilityIds.append(facility.getFacilityId()).append(",");
			}
			if(facilityIds.length()>0){
				facilityIds.deleteCharAt(facilityIds.length()-1);
			}
		}
		return facilityIds.toString().trim();
	}

	/**
	 * @param businessPrivilegeService the businessPrivilegeService to set
	 */
	public void setBusinessPrivilegeService(
			BusinessPrivilegeService businessPrivilegeService) {
		this.businessPrivilegeService = businessPrivilegeService;
	}

	/**
	 * @param facilityService the facilityService to set
	 */
	public void setFacilityService(FacilityService facilityService) {
		this.facilityService = facilityService;
	}
	
}
